/** This class makes all xml-parsing jobs 
 *  @author ChrM */

package com.jtalker.client;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Parser who parse all the message between server and client.
 * 
 * @author Administrator
 */
public class JTalkerParser extends DefaultHandler {
	// Static Vars
	public static final String USER_TYPE_WEBCLIENT = "webclient";
	public static final String USER_TYPE_PERSISTENT = "persistent";
	public static final String USER_TYPE_CHATROOM = "chatroom";

	/**
	 * MEMBER tag includes some members informations. This tag provides follows properties: NAME [, STATUS, PASSWD]
	 */
	public static final String MEMBER = "MEMBER";
	public static final String MEMBER_NAME = "name";
	public static final String MEMBER_STATUS = "status";
	public static final String MEMBER_PASSWD = "passwd";
	public static final String MEMBER_IP = "ip";
	public static final String MEMBER_TYPE = "type";
	public static final String MEMBER_SID = "sid";

	/**
	 * MESSAGE tag includes some messages. This tag provides follows properties: NAME, CONTENT, SENDER, STATUS, TO, TYPE
	 */
	public static final String MESSAGE = "MESSAGE";
	public static final String MESSAGE_CONTENT = "content";
	public static final String MESSAGE_SENDER = "sender";
	public static final String MESSAGE_STATUS = "status";
	public static final String MESSAGE_TO = "to";
	public static final String MESSAGE_TYPE = "type";
	public static final String MESSAGE_SUBJECT = "subject";
	public static final String MESSAGE_ID = "id";

	/** Old style server messages: Error or Info */
	public static final String ERROR = "ERROR";
	public static final String INFO = "INFO";
	public static final int ANSWER_INFO = 0;
	public static final int ANSWER_ERROR = 1;
	public static final int ANSWER_UPDATE = 2;

	/** Config File Tags */
	public static final String CONFIG_ROOT = "JTALKER-CLIENT";
	public static final String CONFIG_ACCOUNT = "ACCOUNT";
	public static final String CONFIG_ACCOUNT_NAME = "name";
	public static final String CONFIG_ACCOUNT_PASSWD = "passwd";
	public static final String CONFIG_SERVER = "SERVER";
	public static final String CONFIG_SERVER_IP = "ip";
	public static final String CONFIG_SERVER_PORT = "port";
	public static final String CONFIG_SERVER_SSL = "ssl";
	public static final String CONFIG_LOOK = "LOOK";
	public static final String CONFIG_LOOK_PLAF = "plaf";
	public static final String CONFIG_LOOK_TEXTCOLOR = "textcolor";
	public static final String CONFIG_LOOK_BGCOLOR = "bgcolor";
	public static final String CONFIG_APPS = "APPS";
	public static final String CONFIG_APPS_BROWSER = "browser";
	public static final String CONFIG_APPS_MAIL = "mail";
	public static final String CONFIG_NOTIFICATION = "NOTIFICATION";
	public static final String CONFIG_NOTIFICATION_USERLIST = "user_list";
	public static final String CONFIG_PLUGINS = "PLUGINS";
	public static final String CONFIG_PLUGINS_DIR = "plugin_dir";
	public static final String CONFIG_PLUGINS_SHORTCUT = "plugin_shortcuts";

	// private members
	private String currentTag = null;
	private String lastAnswer = null;
	private String lastCommand = null;
	private String lastCommandResponse = null;
	private String lastError = null;
	private boolean parsingConfigFile = false;
	private ClientSettings clientSettings = null;
	private int lastAnswerType = JTalkerParser.ANSWER_INFO;
	// private String messageContent = null;
	// private String messageSender = null;
	// private String messageSenderStatus = null;
	// private String messageReceivers = null;
	private ArrayList<OneMember> listMembers = new ArrayList<OneMember>();
	private ArrayList<OneMessage> listMessages = new ArrayList<OneMessage>();

	/** Constructor */
	public JTalkerParser() {
	}

	/** Starts parsing */
	public boolean parsing(String xmlStream) {
		// reset
		this.listMembers.clear();
		this.listMessages.clear();
		this.lastAnswer = null; // Last received server answer
		this.lastAnswerType = JTalkerParser.ANSWER_INFO; // Last received server answer
		this.lastCommand = null; // Last received command
		this.lastCommandResponse = null; // Last received command-response

		try {
			SAXParserFactory fct = SAXParserFactory.newInstance();
			fct.setValidating(false);
			SAXParser saxParser = fct.newSAXParser();
			InputStream is = new ByteArrayInputStream(xmlStream.getBytes("UTF-8"));
			saxParser.parse(is, this);
		} catch (Exception e) {
			// e.printStackTrace();
			return false;
		}

		return true;
	}

	/** Starts parsing of config file */
	public boolean parsing(File configFile) {
		// reset
		this.lastError = null;
		this.parsingConfigFile = false;
		this.clientSettings = new ClientSettings();

		if (!configFile.canRead()) {
			this.lastError = "Can't load file";
			return false;
		}

		try {
			SAXParserFactory fct = SAXParserFactory.newInstance();
			fct.setValidating(false);
			SAXParser saxParser = fct.newSAXParser();
			saxParser.parse(configFile, this);
		} catch (Exception e) {
			// e.printStackTrace();
			return false;
		}

		// Maybe old file format?
		return this.parsingConfigFile;
	}

	/** Gets client settings */
	public ClientSettings getClientSettings() {
		return this.clientSettings;
	}

	// ---------------------------------------------------------------
	/** Gets the message */
	public String getMessage() {
		// return this.messageContent;
		if (this.getAMessage(0) != null)
			return this.getAMessage(0).content;
		else
			return null;
	}

	/** Gets the first Sender (on server site) */
	public String getSender() {
		// return this.messageSender;
		if (this.getAMessage(0) != null)
			return this.getAMessage(0).sender;
		else
			return null;
	}

	/** Gets the first sender status (on server site; in the message Tag) */
	public String getStatus() {
		// return this.messageSenderStatus;
		if (this.getAMessage(0) != null)
			return this.getAMessage(0).senderStatus;
		else
			return "normal";
	}

	/**
	 * Gets the first sender status (in the member Tag). Should be used only for update the own status!
	 */
	public String getMemberStatus() {
		if (this.listMembers.size() == 1)
			return ((OneMember) this.listMembers.get(0)).status;
		else
			return null;
	}

	/**
	 * Gets a list of Receivers for the first message (separated by double semikolon: ';;')
	 */
	public String getRecs() {
		// return this.messageReceivers;
		if (this.getAMessage(0) != null)
			return this.getAMessage(0).messageReceivers;
		else
			return null;
	}

	/** Gets the recieved command */
	public String getCommand() {
		return this.lastCommand;
	}

	/** Gets the recieved command-response */
	public String getCommandResponse() {
		return this.lastCommandResponse;
	}

	/** Gets a member by number */
	public OneMember getAMember(int index) {
		try {
			return this.listMembers.get(index);
		} catch (Exception e) {
			return null;
		}
	}

	/** Gets a message by number */
	public OneMessage getAMessage(int index) {
		try {
			return this.listMessages.get(index);
		} catch (Exception e) {
			return null;
		}
	}

	/** Gets number of members */
	public int getMemberNum() {
		return this.listMembers.size();
	}

	/** Gets number of messages */
	public int getMessagesNum() {
		return this.listMessages.size();
	}

	/** Gets the last Server answer (could be an error or an information) */
	public String getAnswer() {
		return this.lastAnswer;
	}

	/** Gets the last Servers answer type */
	public int getAnswerType() {
		return this.lastAnswerType;
	}

	/** Splits Recs to an ArrayList and get it */
	public String[] getRecsAsArray(int index) {
		return this.getAMessage(index).messageReceivers.split(";;");
	}

	/** Splits Recs to an ArrayList and get it (for the first message) */
	public String[] getRecsAsArray() {
		return this.getAMessage(0).messageReceivers.split(";;");
	}

	// ---------------------------------------------------------------

	/** SAX-methods */
	public void startElement(String _nsURI, String _sName, String _qName, Attributes _attrs) {
		this.currentTag = _qName;
		if (this.currentTag.equalsIgnoreCase(MESSAGE)) {
			// check message type
			String tmpType = _attrs.getValue(MESSAGE_TYPE);
			if (tmpType != null && tmpType.equalsIgnoreCase("error")) {
				this.lastAnswer = _attrs.getValue(MESSAGE_CONTENT);
				this.lastAnswerType = JTalkerParser.ANSWER_ERROR;
			} else if (tmpType != null && tmpType.equalsIgnoreCase("info")) {
				this.lastAnswer = _attrs.getValue(MESSAGE_CONTENT);
				this.lastAnswerType = JTalkerParser.ANSWER_INFO;
			} else if (tmpType != null && tmpType.equalsIgnoreCase("update")) {
				this.lastAnswer = _attrs.getValue(MESSAGE_CONTENT);
				this.lastAnswerType = JTalkerParser.ANSWER_UPDATE;
				// } else if(tmpType != null &&
				// tmpType.equalsIgnoreCase("command")) {
				// this.lastCommand = _attrs.getValue(MESSAGE_CONTENT);
				// } else if(tmpType != null &&
				// tmpType.equalsIgnoreCase("command_response")) {
				// this.lastCommandResponse = _attrs.getValue(MESSAGE_CONTENT);
			} else {
				this.listMessages.add(new OneMessage(_attrs.getValue(MESSAGE_CONTENT), _attrs.getValue(MESSAGE_SENDER),
						_attrs.getValue(MESSAGE_STATUS), _attrs.getValue(MESSAGE_TO), tmpType, _attrs
								.getValue(MESSAGE_SUBJECT), _attrs.getValue(MESSAGE_ID)));
			}
		} else if (this.currentTag.equalsIgnoreCase(MEMBER)) {
			// A Member list is received
			this.listMembers.add(new OneMember(_attrs.getValue(MEMBER_NAME), _attrs.getValue(MEMBER_STATUS), _attrs
					.getValue(MEMBER_PASSWD), _attrs.getValue(MEMBER_IP), _attrs.getValue(MEMBER_TYPE), _attrs
					.getValue(MEMBER_SID)));
		} else if (this.currentTag.equalsIgnoreCase(ERROR)) {
			// OBSOLETE: Server sends an error message
			this.lastAnswer = _attrs.getValue("message");
			this.lastAnswerType = JTalkerParser.ANSWER_ERROR;
		} else if (this.currentTag.equalsIgnoreCase(INFO)) {
			// OBSOLETE: Server sends an info message
			this.lastAnswer = _attrs.getValue("message");
			this.lastAnswerType = JTalkerParser.ANSWER_INFO;
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_ROOT)) {
			// We are parsing the config file
			this.parsingConfigFile = true;
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_ACCOUNT) && this.parsingConfigFile) {
			this.clientSettings.account_name = _attrs.getValue(CONFIG_ACCOUNT_NAME);
			this.clientSettings.account_passwd = _attrs.getValue(CONFIG_ACCOUNT_PASSWD);
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_SERVER) && this.parsingConfigFile) {
			this.clientSettings.server_ip = _attrs.getValue(CONFIG_SERVER_IP);
			this.clientSettings.server_port = _attrs.getValue(CONFIG_SERVER_PORT);
			this.clientSettings.server_ssl = _attrs.getValue(CONFIG_SERVER_SSL);
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_LOOK) && this.parsingConfigFile) {
			this.clientSettings.look_plaf = _attrs.getValue(CONFIG_LOOK_PLAF);
			this.clientSettings.look_textcolor = _attrs.getValue(CONFIG_LOOK_TEXTCOLOR);
			this.clientSettings.look_bgcolor = _attrs.getValue(CONFIG_LOOK_BGCOLOR);
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_APPS) && this.parsingConfigFile) {
			this.clientSettings.apps_browser = _attrs.getValue(CONFIG_APPS_BROWSER);
			this.clientSettings.apps_mail = _attrs.getValue(CONFIG_APPS_MAIL);
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_NOTIFICATION) && this.parsingConfigFile) {
			this.clientSettings.notif_userlist = _attrs.getValue(CONFIG_NOTIFICATION_USERLIST);
		} else if (this.currentTag.equalsIgnoreCase(CONFIG_PLUGINS) && this.parsingConfigFile) {
			this.clientSettings.plugins_dir = _attrs.getValue(CONFIG_PLUGINS_DIR);
			this.clientSettings.plugins_shortcuts = _attrs.getValue(CONFIG_PLUGINS_SHORTCUT);
		}
	}

	/** SAX-methods */
	public void characters(char[] ch, int start, int length) {
	}

	/** SAX-methods */
	public void endElement(String _uri, String _localName, String _qName) {
	}

	/** SAX-error-methods */
	public void error(SAXParseException _e) {
		System.out.println("*** SAX-Parser Error: " + _e.toString());
	}

	/** SAX-error-methods */
	public void fatalError(SAXParseException _e) {
		System.out.println("*** SAX-Parser Error: " + _e.toString());
	}

	/** Gets last Error */
	public String getLastError() {
		return this.lastError;
	}

	/** This inner class represents a member */
	public class OneMember {
		public OneMember(String name, String status, String passwd, String ip, String type, String sid) {
			this.name = name;
			this.status = status;
			this.passwd = passwd;
			this.ip = ip;
			if (type != null) {
				this.type = type;
			} else {
				this.type = USER_TYPE_PERSISTENT;
			}
			this.sid = sid;
		}

		public String name = null; // like "UserA"
		public String status = null; // "normal", "busy", "robot"
		public String passwd = null; // "xxx"
		public String ip = null; // "127.0.0.1"
		public String type = USER_TYPE_PERSISTENT;
		public String sid = null;
	}

	/** This inner class represents a message */
	public class OneMessage {
		public OneMessage(String content, String sender, String senderStatus, String messageReceivers,
				String messageType, String messageSubject, String id) {
			this.content = content;
			this.sender = sender;
			this.senderStatus = senderStatus;
			this.messageReceivers = messageReceivers;
			this.messageType = messageType;
			this.messageSubject = messageSubject;
			this.id = id;
		}

		/** Text */
		public String content = null;
		/** Member name */
		public String sender = null;
		/** "normal", "busy", "robot" */
		public String senderStatus = "normal";
		/** "UserA;;UserB;;UserC" */
		public String messageReceivers = null;
		/** "normal", "error", "info", "command" */
		public String messageType = null;
		/** "picture.jpg" */
		public String messageSubject = null;
		/** "abgh2123" */
		public String id = null;
	}

	/**
	 * This inner class represents the client settings parsed from a config file
	 */
	public class ClientSettings {
		public ClientSettings() {
		}

		public String account_name = System.getProperty("user.name");
		public String account_passwd = "";
		public String server_ip = "localhost";
		public String server_port = "4444";
		public String server_ssl = "false";
		public String look_plaf = "localhost";
		public String look_textcolor = "localhost";
		public String look_bgcolor = "localhost";
		public String apps_browser = "firefox";
		public String apps_mail = "kmail";
		public String notif_userlist = "2";
		public String plugins_dir = System.getProperty("user.home") + File.separator;
		public String plugins_shortcuts = "";
	}
}
